#!/usr/bin/env bash

_VERSION_=1.0

function usage() {
  cat <<EOF
Usage: $0 [-p|--port port]
            [-u|--upload upload]
            [-d|--download download]
            [-t|--target target]
            [-r|--run run]
            [-v|--version version]
            [-h|--help help]
            [--quiet]
            destination
            $(get_usage)
EOF
}

function use_info() {
  cat <<EOF
Concatenate FILE(s), or standard input, to standard output.

  -p,  --port        input port
  -r,  --run         upload file to destination and run it
  -u,  --upload      input upload
  -d,  --download    input download
  -t,  --target      use with upload or download

  -v,  --version     output version information and exit
  -h,  --help        display this help and exit

  --quiet       not show unnecessary log

Examples:
  shcp localhost
  shcp -p 22 abc@127.0.0.1
  shcp -p 2222 abc@127.0.0.1 -u abc.txt -t /data
  shcp -p 2222 abc@127.0.0.1 -u abc.txt -t C:/data
  shcp -p 2222 abc@127.0.0.1 -d abc.txt -t def.txt
EOF
}

if [ "$#" == 0 ]; then usage; use_info; exit 1; fi

function get_host() {
 [ -f /etc/hosts ] && HOST=$(grep -v '^#' /etc/hosts 2>/dev/null | cut -d ' ' -f2-)
 [ -f $HOME/.shell/domains ] && HOST="$HOST $(grep -v '^#' $HOME/.shell/domains 2>/dev/null | grep -E '^(ssh|.*\.ssh\.|.*ssh)$')"
 [ ! -f ~/.ssh/config ] && touch $HOME/.ssh/config && chmod 600 $HOME/.ssh/config
 [ -f ~/.ssh/config ] && HOST="$HOST $(grep '^Host ' $HOME/.ssh/config 2>/dev/null | cut -d ' ' -f2-)"
 [ -d ~/.ssh/config.d ] && HOST="$HOST $(grep '^Host ' $HOME/.ssh/config.d/* 2>/dev/null | cut -d ' ' -f2-)"
 echo $HOST | tr ' ' '\n' | grep -v "*" | sort -u  | grep -E "^$1"
}

function get_param_sort() {
  # $(awk '{print $1}' | grep -oP '\S+(?=\s*\))' | tr '\n|' ' ')
  echo $(cat $0 2>/dev/null | grep -v grep | grep "#.*param:-" | awk '{print $1}' | tr '\n|' ' ')
}

function get_param_total() {
  echo $(cat $0 2>/dev/null | grep -v grep | grep "#.*param:=" | awk '{print $1}' | tr '\n|' ' ')
}

function get_param_command() {
  echo $(cat $0 2>/dev/null | grep -v grep | grep "#.*param:command" | awk '{print $1}' | sed 's/^-*//' | grep -v "*" | tr '\n|' ' ')
}

function get_usage() {
  cat $0 2>/dev/null | grep -v grep | grep -E "# param:(-|=)" | awk '{printf "\t%s\t %s\n",$1,$(NF)}' | tr '_' ' ' | sed 's/comment://g'
}

# 查找符合条件的文件
files_pre=(~/.shell/prepare/shcp.*.pre)
# 检查文件数组中的第一个元素是否是文件
if [ -f "${files_pre[0]}" ]; then
  for file in ~/.shell/prepare/shcp.*.pre; do
    # source 目标文件
    if [ -f "$file" ]; then
      echo "Sourcing $file..."
      source "$file"
    fi
  done
else
  echo "No matching files(pre) found."
fi

# ==============
# Params Store
# ==============
for i in "$@"; do
  case ${_param_} in
    -p    )       _port="$i"                    ;; # param:- comment:input_a_port
    -u    )     _upload="$i"; _command=scp_up   ;; # param:- comment:
    -d    )   _download="$i"; _command=scp_down ;; # param:- comment:
    -c    )    _command="$i"                    ;; # param:- comment:
    -t    )     _target="$i"                    ;; # param:- comment:
    -td   ) _target_dir="$i"                    ;; # param:- comment:
    -n    )     _number="$i"                    ;; # param:- comment:
    -i    )    _id_file="$i"                    ;; # param:- comment:
    -user )       _user="$i@"                   ;; # param:- comment:
  esac
  _param_="$i"
  case $i in
    --help|-h    ) usage; use_info; exit 0           ;; # param:= comment:
    --version|-v ) echo Version:${_VERSION_}; exit 0 ;; # param:= comment:

    --async      )    _async="on"             ;; # param:- comment: 激活异步处理
    --copy       )  _copy_id="on"             ;; # param:- comment: 拷贝ssh key
    --no-senv    )  _no_senv="on"             ;; # param:- comment:
    --quiet      )    _quiet="on"             ;; # param:- comment: 静默少打显示类日志
    --root       )     _user="root@"          ;; # param:- comment: 使用root用户
    --host       )    get_host; exit 0        ;; # param:= comment:
    --list       ) _command="list-host"       ;; # param:= comment: list host
    --param-s    ) get_param_sort; exit 0     ;; # param:= comment:
    --param-c    ) get_param_command; exit 0  ;; # param:= comment:
    --param-t    ) get_param_total; exit 0    ;; # param:= comment:

    --init-senv           ) _command="${i#*--}"; prepare-init-senv;           ;; # param:- param:command comment:
    --init-shcp           ) _command="${i#*--}"                               ;; # param:- param:command comment:
    --init-alias          ) _command="${i#*--}"                               ;; # param:- param:command comment:
    --init-kw             ) _command="${i#*--}"                               ;; # param:- param:command comment:
    --init-ks-setup       ) _command="${i#*--}"                               ;; # param:- param:command comment:
    --init-ks-install-frp ) _command="${i#*--}"; prepare-init-ks-install-frp; ;; # param:- param:command comment:
    --init-vm-user        ) _command="${i#*--}"                               ;; # param:- param:command comment:
    --init-vm-dir         ) _command="${i#*--}"                               ;; # param:- param:command comment:
    --init-vm-conf        ) _command="${i#*--}"                               ;; # param:- param:command comment:
    --init-vm-installqemu ) _command="${i#*--}"                               ;; # param:- param:command comment:
    --init-vm-fwcxX       ) _command="${i#*--}"                               ;; # param:- param:command comment:
    --init-vm-hugepage    ) _command="${i#*--}"                               ;; # param:- param:command comment:
    --init-vm-netplan     ) _command="${i#*--}"                               ;; # param:- param:command comment:
    --init-vm-check       ) _command="${i#*--}"                               ;; # param:- param:command comment:

  esac
  [ -z $_destination ] && _destination=$i
done

[ -z "${_command}" ] && _command=ssh

function param_id_file() {
  local dst_host=$1
  local dst_id_dir_count=$(ls -d ~/.ssh/*/ 2>/dev/null | grep -v "/config.d/" | grep -E "/${dst_host%%_*}/$" | wc -l )
  local dst_id_dir=$(ls -d ~/.ssh/*/ 2>/dev/null | grep -v "/config.d/" | grep -E "/${dst_host%%_*}/$")
  ID_FILE=~/.ssh/id_ed25519
  [[ -n "${dst_id_dir}" && "${dst_id_dir_count}"=="1" ]] && TARGET_DIR=${_target_dir} && ID_FILE=${dst_id_dir}id_ed25519
  [ -n "$_id_file" ] && ID_FILE="$_id_file"
  [ -n "$ID_FILE" ] && ParamIdFile=" -i $ID_FILE"
}
function param_init() {
  # ParamDestination
  if [ -n "$(echo "$_destination" | grep "@")" ];then _user=${_destination%@*}@; _destination=${_destination#*@}; fi
  # ParamPort
  if [ -n "$_port" ];then
    if [[ -n "$_upload" || -n "$_download" ]];then
      ParamPort=" -P $_port"
    else
      ParamPort=" -p $_port"
    fi
  fi
  # TargetDir
  [ -z "$_target_dir" ] && _target_dir=kw/
  [ "${_target_dir}" == "${_target_dir%/*}" ] && _target_dir=${_target_dir}/
  [ -z "$(echo $_destination | grep -E "_")" ] && _target_dir=
  # ParamIdFile
  param_id_file ${_destination}
  # Target
  [[ -z "$_target" && -z "$_download" ]] && _target=${TARGET_DIR}
}

function scp_up() {
  param_init
  printf "[%2s] " $now_number
  echo -e "\033[33mNowUpload\033[m(\033[35m$_upload\033[m) to (\033[34m$_user$host:$_target\033[m)"
  scp$ParamIdFile$ParamPort -r $_upload "${_user}${host}:$_target"
  local exit_code=$?
  if [ -z "${_quiet}" ];then
    printf "\n\e[m<<<<<<<<<<<<<<<<<<<<<<<< exit ${exit_code}\n"
  else
    printf "\n\e[mexit ${exit_code}\n"
  fi
  return ${exit_code}
}

function scp_down() {
  param_init
  printf "[%2s] " $now_number
  echo -e "\033[33mNowDownload\033[m(\033[35m$_user$host:$_download\033[m) to (\033[34m$_target\033[m)"
  if [ -z "$_target" ]; then
    _target=.
  fi
  scp$ParamIdFile$ParamPort -r "${_user}${host}:$_download" "$_target"
  local exit_code=$?
  if [ -z "${_quiet}" ];then
    printf "\n\e[m<<<<<<<<<<<<<<<<<<<<<<<< exit ${exit_code}\n"
  else
    printf "\n\e[mexit ${exit_code}\n"
  fi
  return ${exit_code}
}

function ssh_run() {
  param_init
  ParamPort=$(echo "$ParamPort" | sed 's/ -P / -p /g')
  printf "[%2s] " $now_number
  printf "\e[33mNowSSH\e[m[\e[35m${host}\e[m] Run: (\e[34m"
  echo -e "$_target\c"
  if [ -z "${_quiet}" ];then
    printf "\e[m)\n>>>>>>>>>>>>>>>>>>>>>>>>\e[m\n";
  else
    printf "\e[m)\n";
  fi
  ssh -o ConnectTimeout=2$ParamIdFile$ParamPort ${_user}${host} "$_target"
  local exit_code=$?
  if [ -z "${_quiet}" ];then
    printf "\n\e[m<<<<<<<<<<<<<<<<<<<<<<<< exit ${exit_code}\n"
  else
    printf "\n\e[mexit ${exit_code}\n"
  fi
  return ${exit_code}
}

function ssh_host() {
  param_init
  if [ -n "$(echo "$host" | grep "@")" ];then _user=${host%@*}@; host=${host#*@}; fi
  local rcfile=".profile"
  param_id_file ${host}
  if [ -z ${_quiet} ]; then echo -e "\033[32mUseId\033[m(\033[34m$ID_FILE\033[m)"; fi
  [ -n "$_copy_id" ] && ssh-copy-id$ParamIdFile.pub$ParamPort ${_user}${host}
  printf "[%2s] " $now_number
  printf "\033[32mNowSSH\033[m[\033[35m${_user}${host}\033[m]:$_target_dir\n"
  if [ -n "$(echo $host | grep -E "_win$")" ]; then
    ssh$ParamIdFile$ParamPort -t ${_user}${host}
  else
    [ -z "$(echo ${host} | grep -E "^router-")" ] && rcfile="~/${_target_dir}.shell/senv"
    [ -n "$_no_senv" ] && senv_command= || senv_command="HOST_TAG=${host} TARGET_DIR=$_target_dir bash --rcfile ${rcfile}"
    ssh$ParamIdFile$ParamPort -t ${_user}${host} "$senv_command"
  fi
  return 0
}

# 查找符合条件的文件
files_func=(~/.shell/func/shcp.*.func)
# 检查文件数组中的第一个元素是否是文件
if [ -f "${files_func[0]}" ]; then
  for file in ~/.shell/func/shcp.*.func; do
    # source 目标文件
    if [ -f "$file" ]; then
      echo "Sourcing $file..."
      source "$file"
    fi
  done
else
  echo "No matching files(func) found."
fi

function run_task() {
  if [ -n "${_async}" ]; then
    "$@" &
  else
    "$@"
  fi
  return 0
}

if [ -z "${_quiet}" ];then
  echo -e '┌───────────────────────────────'
  echo -e '│ * destination   :'"\033[33m${_destination}\033[m"
  echo -e '│ * command[-c]   :'"\033[33m${_command}\033[m"
  echo -e '│ * target[-t]    :'"\033[33m${_target}\033[m"
  echo -e '│ * async[--async]:'"\033[33m$(if [ -z ${_async} ];then echo false; else echo true; fi)\033[m"
  echo -e '└───────────────────────────────'
fi

function _get_list_str() {
  local input=$1
  start_range=$(echo $input | cut -d '-' -f1)
  end_range=$(echo $input | cut -d '-' -f2)

  if [ -z "$end_range" ];then
    end_range="-"
  fi
  if [ -z "$start_range" ];then
    start_range="0"
  fi

  if [ "$end_range" == "-" ];then
    echo "$start_range -"
    return 0
  fi
  if [ "$start_range" -gt "$end_range" ];then
    for((i=start_range; i>=end_range; i--));do
      echo -n "$i"
    done
  else
    for((i=start_range; i<=end_range; i++));do
      echo -n "$i "
    done
  fi
  return 0
}
function _check_in_list_str() {
  local list=($(_get_list_str $1))
  local found=0
  for element in "${list[@]}"; do
    if [ "$element" = "$2" ];then
      found=1; break
    fi
    if [ "$element" = "-" ];then
      if [ $2 -gt "${list[0]}" ];then
        found=1; break
      else
        found=0; break
      fi
    fi
  done
  if [ $found -eq 1 ];then
    return 0
  else
    return 1
  fi
}


if [ -n "$(echo "$_destination" | grep "@")" ];then _user=${_destination%@*}@; _destination=${_destination#*@}; fi
destination_hosts=$(get_host ${_destination})
[ "$(echo ${destination_hosts} | tr ' ' '\n' | grep "^${_destination}$")" == "${_destination}" ] && destination_hosts=${_destination}
[ -z "$destination_hosts" ] && printf "\033[31mWarning: no host selected, do direct\033[m\n" && destination_hosts=${_destination}

# ===============
# Process
# ===============
now_number=0
for host in $destination_hosts; do
  if [ "${_number}" != "" ];then
    if [ "${_number}" == "${now_number}" ];then
      echo -e "\033[31mOnlyDo\033[m:\033[35m${_region}\033[m[${now_number}] \033[34m${host_ip}\033[m"
      _once_=true
    else
      _check_in_list_str ${_number} ${now_number}
      if [ $? -eq 1 ];then
        now_number=$((now_number + 1))
        continue
      fi
    fi
  fi

  case ${_command} in
    get_host          )          run_task ${_command} ;; # param:command comment:
    scp_up            )          run_task ${_command} ;; # param:command comment:
    scp_down          )          run_task ${_command} ;; # param:command comment:

    init-*            )          run_task ${_command} ;; # param:command comment:

    ssh_run|run       )          run_task ssh_run     ;; # param:command comment:
    ssh               )          ssh_host             ;; # param:command comment:
    ls|list|list-host )                                  # param:command comment:
      echo "$(printf "%-4s" "[${now_number}]")" "$(printf "%-16s" "${host}")"
      ;;
    *) echo -e "\033[31mError: unknuwn command\033[m ${_command}"; exit 1 ;;
  esac
  if [ $? != 0 ];then
    echo "???"
    exit 1;
  fi
  if [ -n "$_once_" ];then wait; exit 0; else now_number=$((now_number + 1)); fi
  # wait process done
  if [ $((now_number % 3000)) -eq 0 ]; then
    wait
  fi
done
wait
